package org.mnswf.scorchedearth.managers {
	
	import flash.net.registerClassAlias;
	
	import mdm.Dialogs;
	import mdm.Network;
	import mx.collections.ArrayCollection;
	import org.mnswf.events.UDPDataEvent;
	import org.mnswf.net.UDPPacket;
	import org.mnswf.net.UDPSocket;
	import mx.controls.Alert;
	import flash.utils.setInterval;
	import flash.utils.clearInterval;
	import flash.events.EventDispatcher;
	import org.mnswf.scorchedearth.messages.StartGameMessage;
	import org.mnswf.scorchedearth.messages.ShotActionMessage;
	import org.mnswf.scorchedearth.messages.PlayerMessage;
	import org.mnswf.scorchedearth.messages.FailureMessage;
	import org.mnswf.scorchedearth.events.ShotEvent;
	import org.mnswf.scorchedearth.events.StartGameEvent;
	
	/**
	 * The host listens on port 4000 and sends on port 4001.
	 * The guest listens on port 4001 and sends on port 4000.
	 * 
	 * @author Danny Patterson
	 */
	public class ConnectionManager extends EventDispatcher {
		
		private var socket:UDPSocket;
		private var hostBroadcastInterval:Number;
		
		[Bindable]
		public var isHost:Boolean;
		[Bindable]
		public var hosts:ArrayCollection;
		[Bindable]
		public var opponent:PlayerMessage;
		[Bindable]
		public var me:PlayerMessage;
		
		public function ConnectionManager() {
			hosts = new ArrayCollection();
			socket = new UDPSocket();
			socket.addEventListener(UDPDataEvent.DATA, onDataReceived, false, 0 ,true);
			// Register Message Types
			registerClassAlias("org.mnswf.scorchedearth.messages::PlayerMessage", PlayerMessage);
			registerClassAlias("org.mnswf.scorchedearth.messages::ShotActionMessage", ShotActionMessage);
			registerClassAlias("org.mnswf.scorchedearth.messages::StartGameMessage", StartGameMessage);
			registerClassAlias("org.mnswf.scorchedearth.messages::FailureMessage", FailureMessage);
		}
		
		private function onDataReceived(event:UDPDataEvent):void {
			if(event.packet.message is ShotActionMessage) {
				dispatchEvent(new ShotEvent(ShotEvent.SHOT, ShotActionMessage(event.packet.message)));
			}else if(event.packet.message is PlayerMessage) {
				if(isHost) {
					if(opponent == null) {
						opponent = PlayerMessage(event.packet.message);
						clearInterval(hostBroadcastInterval)
					}else {
						var error:FailureMessage = new FailureMessage();
						error.code = FailureMessage.BLOCKED_CONNECTION;
						error.description = "This host already has an oponent."
						var packet:UDPPacket = new UDPPacket();
						packet.message = error;
						socket.send(PlayerMessage(event.packet.message).ipAddress, 4000, packet);
					}
				}else {
					var player:PlayerMessage = PlayerMessage(event.packet.message);
					var isPlayerFound:Boolean = false;
					for(var i:Number = 0; i < hosts.length; i++) {
						if(PlayerMessage(hosts.getItemAt(i)).ipAddress == player.ipAddress) {
							isPlayerFound = true;
							break;
						}
					}
					if(!isPlayerFound) hosts.addItem(event.packet.message);
				}
			}else if(event.packet.message is StartGameMessage) {
				if(!isHost) {
					dispatchEvent(new StartGameEvent(StartGameEvent.START_GAME, StartGameMessage(event.packet.message)));
				}
			}else if(event.packet.message is FailureMessage) {
				switch(FailureMessage(event.packet.message).code) {
					case FailureMessage.BLOCKED_CONNECTION:
						reset();
						break;
				}
				Alert.show(FailureMessage(event.packet.message).description);
			}
		}
		
		/**
		 * Begins listening for hosts that broadcast their game information.
		 */
		public function listenForHosts():void {
			isHost = false;
			socket.start(4000);
		}
		
		/**
		 * Sends a request to a host to join their game.
		 */
		public function joinHost(host:PlayerMessage):void {
			this.opponent = host;
			var packet:UDPPacket = new UDPPacket();
			packet.message = me;
			socket.send(opponent.ipAddress, 4001, packet);
		}
		
		/**
		 * Sets up the application in host mode by sending out a broadcast message on port 4000
		 * to all user's waiting for a host and listening for responses on port 4001.
		 */
		public function hostGame():void {
			if(!isHost) socket.stop();
			isHost = true;
			if(isNaN(hostBroadcastInterval)) {
				hostBroadcastInterval = setInterval(hostGame, 3000);
			}
			var packet:UDPPacket = new UDPPacket();
			packet.message = me;
			socket.broadcast(4000, packet);
			socket.start(4001);
		}
		
		/**
		 * Resets the connection manager to its initial state.
		 */
		public function reset():void {
			opponent = null;
			isHost = false;
			hosts.removeAll();
			socket.stop();
		}
		
		/**
		 * Sends a request to initiate the start of the game;
		 * from the host to the guest.
		 */
		public function startGame():void {
			if(isHost) {
				var startGame:StartGameMessage = new StartGameMessage();
				var packet:UDPPacket = new UDPPacket();
				packet.message = startGame;
				socket.send(opponent.ipAddress, 4000, packet);
			}
		}
		
		/**
		 * Sends a ShotAction to your opponent.
		 * 
		 * @param shotAction (ShotActionMessage)
		 */
		public function shoot(shotAction:ShotActionMessage):void {
			var packet:UDPPacket = new UDPPacket();
			packet.message = shotAction;
			var port:int = (isHost) ? 4000 : 4001;
			socket.send(opponent.ipAddress, port, packet);
		}
		
	}
	
}